// Magic Software, Inc.
// http://www.magic-software.com
// Copyright (c) 2000, All Rights Reserved
//
// Source code from Magic Software is supplied under the terms of a license
// agreement and may not be copied or disclosed except in accordance with the
// terms of that agreement.  The various license agreements may be found at
// the Magic Software web site.  This file is subject to the license
//
// RESTRICTED USE SOURCE CODE
// http://www.magic-software.com/License/restricted.pdf

#include "MgcBezierCylinder2.h"
#include "MgcTriMesh.h"

MgcImplementRTTI(MgcBezierCylinder2,MgcBezierCylinder);
MgcImplementStream(MgcBezierCylinder2);

// macros for initialization in the subdivision
#define XA(i) akCtrlPoint[m_auiIndex[i]]
#define CA(i) akCtrlColor[m_auiIndex[i]]
#define TA(i) akCtrlTexture[m_auiIndex[i]]

//----------------------------------------------------------------------------
MgcBezierCylinder2::MgcBezierCylinder2 (unsigned int* auiIndex)
    :
    MgcBezierCylinder(2,6,auiIndex)
{
}
//----------------------------------------------------------------------------
MgcBezierCylinder2::MgcBezierCylinder2 ()
{
}
//----------------------------------------------------------------------------
void MgcBezierCylinder2::InitializePoints (const MgcVector3* akCtrlPoint,
    IntervalParameters& rkIP)
{
    // Xss[t]
    rkIP.m_aakXss[0] = 6.0*(XA(0) - 2.0*XA(1) + XA(2));
    rkIP.m_aakXss[1] = 6.0*(XA(3) - 2.0*XA(4) + XA(5));
}
//----------------------------------------------------------------------------
void MgcBezierCylinder2::InitializeColors (const MgcColor* akCtrlColor,
    IntervalParameters& rkIP)
{
    // Css[t]
    rkIP.m_aakCss[0] = 6.0*(CA(0) - 2.0*CA(1) + CA(2));
    rkIP.m_aakCss[1] = 6.0*(CA(3) - 2.0*CA(4) + CA(5));
}
//----------------------------------------------------------------------------
void MgcBezierCylinder2::InitializeTextures (const MgcVector2* akCtrlTexture,
    IntervalParameters& rkIP)
{
    // Tss[t]
    rkIP.m_aakTss[0] = 6.0*(TA(0) - 2.0*TA(1) + TA(2));
    rkIP.m_aakTss[1] = 6.0*(TA(3) - 2.0*TA(4) + TA(5));
}
//----------------------------------------------------------------------------
void MgcBezierCylinder2::Tessellate (unsigned int uiLevel,
    const MgcVector3* akCtrlPoint, const MgcColor* akCtrlColor,
    const MgcVector2* akCtrlTexture, MgcTriMesh* pkMesh,
    unsigned int& ruiVertexStart, unsigned int& ruiTriangleStart)
{
    GenerateConnectivity(uiLevel,pkMesh,ruiTriangleStart);

    // number of vertices in the subdivision
    unsigned int uiQuantity = GetVerticesPerCylinderPatch(uiLevel,
        m_uiCylinderLevel);

    // indices of four corners of patch, I[s][t]
    unsigned int uiTwoPowL = (1 << uiLevel);
    unsigned int uiTwoPowC = (1 << m_uiCylinderLevel);
    IntervalParameters kIP;
    kIP.m_uiI00 = 0;
    kIP.m_uiI01 = uiTwoPowC*(uiTwoPowL + 1);
    kIP.m_uiI10 = uiTwoPowL;
    kIP.m_uiI11 = kIP.m_uiI01 + uiTwoPowL;

    // vertices for subdivision
    MgcVector3* akX = pkMesh->Vertices() + ruiVertexStart;
    akX[kIP.m_uiI00] = XA(0);
    akX[kIP.m_uiI01] = XA(3);
    akX[kIP.m_uiI10] = XA(2);
    akX[kIP.m_uiI11] = XA(5);

    // derivatives for subdivision (for normal vectors)
    MgcVector3* akXs;
    MgcVector3* akXt;
    if ( pkMesh->Normals() )
    {
        akXs = new MgcVector3[uiQuantity];
        akXs[kIP.m_uiI00] = 2.0*(XA(1) - XA(0));
        akXs[kIP.m_uiI01] = 2.0*(XA(4) - XA(3));
        akXs[kIP.m_uiI10] = 2.0*(XA(2) - XA(1));
        akXs[kIP.m_uiI11] = 2.0*(XA(5) - XA(4));

        akXt = new MgcVector3[uiQuantity];
        akXt[kIP.m_uiI00] = 2.0*(XA(3) - XA(0));
        akXt[kIP.m_uiI01] = akXt[kIP.m_uiI00];
        akXt[kIP.m_uiI10] = 2.0*(XA(5) - XA(2));
        akXt[kIP.m_uiI11] = akXt[kIP.m_uiI10];
    }
    else
    {
        akXs = 0;
        akXt = 0;
    }

    // colors for subdivision
    MgcColor* akColor;
    if ( pkMesh->Colors() )
    {
        akColor = pkMesh->Colors() + ruiVertexStart;
        akColor[kIP.m_uiI00] = CA(0);
        akColor[kIP.m_uiI01] = CA(3);
        akColor[kIP.m_uiI10] = CA(2);
        akColor[kIP.m_uiI11] = CA(5);
    }
    else
    {
        akColor = 0;
    }

    // textures for subdivision
    MgcVector2* akTexture;
    if ( pkMesh->Textures() )
    {
        akTexture = pkMesh->Textures() + ruiVertexStart;
        akTexture[kIP.m_uiI00] = TA(0);
        akTexture[kIP.m_uiI01] = TA(3);
        akTexture[kIP.m_uiI10] = TA(2);
        akTexture[kIP.m_uiI11] = TA(5);
    }
    else
    {
        akTexture = 0;
    }

    // recursive subdivision
    if ( uiLevel > 0 || m_uiCylinderLevel > 0 )
    {
        InitializePoints(akCtrlPoint,kIP);

        if ( akCtrlColor )
            InitializeColors(akCtrlColor,kIP);

        if ( akCtrlTexture )
            InitializeTextures(akCtrlTexture,kIP);
    }

    if ( uiLevel > 0 )
        SubdivideBoundary(--uiLevel,0.25,akX,akXs,akXt,akColor,akTexture,kIP);

    if ( m_uiCylinderLevel > 0 )
    {
        SubdivideCylinder(m_uiCylinderLevel-1,akX,akXs,akXt,akColor,
            akTexture,0,kIP.m_uiI01,uiTwoPowL);
    }

    // calculate unit-length normals from derivative vectors
    if ( pkMesh->Normals() )
    {
        MgcVector3* akNormal = pkMesh->Normals() + ruiVertexStart;
        for (unsigned int uiI = 0; uiI < uiQuantity; uiI++)
            akNormal[uiI] = akXs[uiI].UnitCross(akXt[uiI]);
        delete[] akXs;
        delete[] akXt;
    }

    ruiVertexStart += uiQuantity;
}
//----------------------------------------------------------------------------
void MgcBezierCylinder2::SubdivideBoundary (unsigned int uiLevel,
    MgcReal fDSqr, MgcVector3* akX, MgcVector3* akXs, MgcVector3* akXt,
    MgcColor* akColor, MgcVector2* akTexture, IntervalParameters& rkIP)
{
    // subdivision indices
    unsigned int uiIM0 = (rkIP.m_uiI00 + rkIP.m_uiI10) >> 1;
    unsigned int uiIM1 = (rkIP.m_uiI01 + rkIP.m_uiI11) >> 1;

    // vertices
    akX[uiIM0] = 0.5*(akX[rkIP.m_uiI00] + akX[rkIP.m_uiI10] -
        fDSqr*rkIP.m_aakXss[0]);
    akX[uiIM1] = 0.5*(akX[rkIP.m_uiI01] + akX[rkIP.m_uiI11] -
        fDSqr*rkIP.m_aakXss[1]);

    // derivatives (for normal vectors)
    if ( akXs )
    {
        akXs[uiIM0] = 0.5*(akXs[rkIP.m_uiI00] + akXs[rkIP.m_uiI10]);
        akXs[uiIM1] = 0.5*(akXs[rkIP.m_uiI01] + akXs[rkIP.m_uiI11]);
        akXt[uiIM0] = akX[uiIM1] - akX[uiIM0];
        akXt[uiIM1] = akXt[uiIM0];
    }

    // colors
    if ( akColor )
    {
        akColor[uiIM0] = 0.5*(akColor[rkIP.m_uiI00] + akColor[rkIP.m_uiI10] -
            fDSqr*rkIP.m_aakCss[0]);
        akColor[uiIM1] = 0.5*(akColor[rkIP.m_uiI01] + akColor[rkIP.m_uiI11] -
            fDSqr*rkIP.m_aakCss[1]);
    }

    // textures
    if ( akTexture )
    {
        akTexture[uiIM0] = 0.5*(akTexture[rkIP.m_uiI00] +
            akTexture[rkIP.m_uiI10] - fDSqr*rkIP.m_aakTss[0]);
        akTexture[uiIM1] = 0.5*(akTexture[rkIP.m_uiI01] +
            akTexture[rkIP.m_uiI11] - fDSqr*rkIP.m_aakTss[1]);
    }

    // recurse on two children
    if ( uiLevel > 0 )
    {
        uiLevel--;
        fDSqr *= 0.25;

        IntervalParameters kSubIP;

        // subinterval [s0,sM]
        kSubIP.m_uiI00 = rkIP.m_uiI00;
        kSubIP.m_uiI01 = rkIP.m_uiI01;
        kSubIP.m_uiI10 = uiIM0;
        kSubIP.m_uiI11 = uiIM1;

        kSubIP.m_aakXss[0] = rkIP.m_aakXss[0];
        kSubIP.m_aakXss[1] = rkIP.m_aakXss[1];

        if ( akColor )
        {
            kSubIP.m_aakCss[0] = rkIP.m_aakCss[0];
            kSubIP.m_aakCss[1] = rkIP.m_aakCss[1];
        }

        if ( akTexture )
        {
            kSubIP.m_aakTss[0] = rkIP.m_aakTss[0];
            kSubIP.m_aakTss[1] = rkIP.m_aakTss[1];
        }

        SubdivideBoundary(uiLevel,fDSqr,akX,akXs,akXt,akColor,akTexture,
            kSubIP);

        // subinterval [sM,s1]
        kSubIP.m_uiI00 = uiIM0;
        kSubIP.m_uiI01 = uiIM1;
        kSubIP.m_uiI10 = rkIP.m_uiI10;
        kSubIP.m_uiI11 = rkIP.m_uiI11;

        kSubIP.m_aakXss[0] = rkIP.m_aakXss[0];
        kSubIP.m_aakXss[1] = rkIP.m_aakXss[1];

        if ( akColor )
        {
            kSubIP.m_aakCss[0] = rkIP.m_aakCss[0];
            kSubIP.m_aakCss[1] = rkIP.m_aakCss[1];
        }

        if ( akTexture )
        {
            kSubIP.m_aakTss[0] = rkIP.m_aakTss[0];
            kSubIP.m_aakTss[1] = rkIP.m_aakTss[1];
        }

        SubdivideBoundary(uiLevel,fDSqr,akX,akXs,akXt,akColor,akTexture,
            kSubIP);
    }
}
//----------------------------------------------------------------------------
void MgcBezierCylinder2::SubdivideCylinder (unsigned int uiCLevel,
    MgcVector3* akX, MgcVector3* akXs, MgcVector3* akXt, MgcColor* akColor,
    MgcVector2* akTexture, unsigned int uiI0, unsigned int uiI1,
    unsigned int uiTwoPowL)
{
    // subdivision index
    unsigned int uiIM = (uiI0 + uiI1) >> 1;

    unsigned int uiJ0 = uiI0, uiJM = uiIM, uiJ1 = uiI1;
    unsigned int uiJLast = uiJM + uiTwoPowL;
    for (/**/; uiJM <= uiJLast; uiJ0++, uiJM++, uiJ1++)
    {
        // vertices
        akX[uiJM] = 0.5*(akX[uiJ0] + akX[uiJ1]);

        // derivatives (for normal vectors)
        if ( akXs )
        {
            akXs[uiJM] = 0.5*(akXs[uiJ0] + akXs[uiJ1]);
            akXt[uiJM] = akXt[uiJ0];
        }

        // colors
        if ( akColor )
            akColor[uiJM] = 0.5*(akColor[uiJ0] + akColor[uiJ1]);

        // textures
        if ( akTexture )
            akTexture[uiJM] = 0.5*(akTexture[uiJ0] + akTexture[uiJ1]);
    }

    // recurse on two children
    if ( uiCLevel > 0 )
    {
        uiCLevel--;

        // subinterval [t0,tM]
        SubdivideCylinder(uiCLevel,akX,akXs,akXt,akColor,akTexture,uiI0,
            uiIM,uiTwoPowL);

        // subinterval [tM,t1]
        SubdivideCylinder(uiCLevel,akX,akXs,akXt,akColor,akTexture,uiIM,
            uiI1,uiTwoPowL);
    }
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// streaming
//----------------------------------------------------------------------------
MgcObject* MgcBezierCylinder2::Factory (MgcStream& rkStream)
{
    MgcBezierCylinder2* pkObject = new MgcBezierCylinder2;
    MgcStream::Link* pkLink = new MgcStream::Link(pkObject);
    pkObject->Load(rkStream,pkLink);
    return pkObject;
}
//----------------------------------------------------------------------------
void MgcBezierCylinder2::Load (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcBezierCylinder::Load(rkStream,pkLink);
}
//----------------------------------------------------------------------------
void MgcBezierCylinder2::Link (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcBezierCylinder::Link(rkStream,pkLink);
}
//----------------------------------------------------------------------------
bool MgcBezierCylinder2::Register (MgcStream& rkStream)
{
    return MgcBezierCylinder::Register(rkStream);
}
//----------------------------------------------------------------------------
void MgcBezierCylinder2::Save (MgcStream& rkStream)
{
    MgcBezierCylinder::Save(rkStream);
}
//----------------------------------------------------------------------------
